﻿
function createLocationBox() {
    var _this = new Object();
    _this.lat = null;
    _this.lon = null;
    _this.locationChangeEventHandlers = [];
    // delegate :: LocationBox -> {lat,long} -> void
    _this.AddLocationChangeEventListener = function ( delegate ) {
        _this.locationChangeEventHandlers.push( delegate )
    };
    _this.OnLocationChange = function() {
        for(var i = 0; i < _this.locationChangeEventHandlers.length; i++) {
            _this.locationChangeEventHandlers[i].call( null , _this, {lat: _this.lat, lon: _this.lon});
        }
    };
    _this.SetLocation = function( _lat , _lon ) {
        _this.lat = _lat;
        _this.lon = _lon;
        _this.OnLocationChange();
    };
    return _this;
}

function createInput(parent) {
    var res = document.createElement('input');
    res.setAttribute('type','text');
    res.className = 'editboxShort';
    parent.appendChild(res);
    return res;
}

function createDmsBox( locBox , canvas ) {    
    var tab = canvas;
    
    var latDiv = document.createElement('div');
    var latLabel = document.createElement('span');
    latLabel.className = 'label';
    latLabel.style.paddingRight = '20px';
    latLabel.innerHTML = 'Lat';
    latDiv.appendChild(latLabel);
    
    tab.edLatDegrees = createInput(latDiv);
    tab.edLatMinutes = createInput(latDiv);
    tab.edLatSeconds = createInput(latDiv);          
    
    var longDiv = document.createElement('div');     
    var longLabel = document.createElement('span');
    longLabel.className = 'label';
    longLabel.style.paddingRight = '10px';
    longLabel.innerHTML = 'Long';
    longDiv.appendChild(longLabel);

    tab.edLongDegrees = createInput(longDiv);
    tab.edLongMinutes = createInput(longDiv);
    tab.edLongSeconds = createInput(longDiv);    
    
    tab.appendChild(latDiv);
    tab.appendChild(longDiv);
    
    tab.SubmitValues = function(){
        // TODO Validation
            
        var dmsLat = {degrees: parseFloat(DefaultIfEmpty(tab.edLatDegrees.value,'0')), 
                      minutes: parseFloat(DefaultIfEmpty(tab.edLatMinutes.value,'0')), 
                      seconds: parseFloat(DefaultIfEmpty(tab.edLatSeconds.value,'0'))};
        var dmsLon = {degrees: parseFloat(DefaultIfEmpty(tab.edLongDegrees.value,'0')), 
                      minutes: parseFloat(DefaultIfEmpty(tab.edLongMinutes.value,'0')), 
                      seconds: parseFloat(DefaultIfEmpty(tab.edLongSeconds.value,'0'))};
        var dmsCoords = {lat: dmsLat, lon: dmsLon};
        
        var decimalCoords = DmsToDecimal( dmsCoords );
        
        locBox.SetLocation( decimalCoords.lat , decimalCoords.lon );
    };

    locBox.AddLocationChangeEventListener( function( lb , point ) {
        pointDms = DecimalToDms( point );

        tab.edLatDegrees.value = pointDms.lat.degrees;
        tab.edLatMinutes.value = pointDms.lat.minutes;
        tab.edLatSeconds.value = pointDms.lat.seconds;
        
        tab.edLongDegrees.value = pointDms.lon.degrees;
        tab.edLongMinutes.value = pointDms.lon.minutes;
        tab.edLongSeconds.value = pointDms.lon.seconds;
    });
    
    tab.edLatDegrees.onblur = tab.SubmitValues;
    tab.edLatMinutes.onblur = tab.SubmitValues;
    tab.edLatSeconds.onblur = tab.SubmitValues;
    tab.edLongDegrees.onblur = tab.SubmitValues;
    tab.edLongMinutes.onblur = tab.SubmitValues;
    tab.edLongSeconds.onblur = tab.SubmitValues;
}

function createDecimalBox( locBox , canvas ) {

    var tab = canvas;
    
    var latLongDiv = document.createElement('div');
    
    var latLabel = document.createElement('span');
    latLabel.className = 'label';
    latLabel.innerHTML = 'Lat';
    latLongDiv.appendChild(latLabel);
    
    tab.edDecimalLat = createInput(latLongDiv);
    
    var longLabel = document.createElement('span');
    longLabel.className = 'label';
    longLabel.innerHTML = 'Long';
    latLongDiv.appendChild(longLabel);

    tab.edDecimalLong = createInput(latLongDiv);
    
    tab.appendChild(latLongDiv);
    
    tab.SubmitValues = function(){
        var decimalLat = DefaultIfEmpty(tab.edDecimalLat.value,'0');
        var decimalLong = DefaultIfEmpty(tab.edDecimalLong.value,'0');

        if(!isValidDecimalLat(decimalLat))
        {
            alert("Invalid Decimal Latitude entered");
            return;
        }
        
        if(!isValidDecimalLong(decimalLong))
        {
            alert("Invalid Decimal Longitude entered");
            return;
        }
    
        locBox.SetLocation( parseFloat(decimalLat) , parseFloat(decimalLong) );
    }
    
    locBox.AddLocationChangeEventListener( function( lb , point ) {
        tab.edDecimalLat.value = point.lat;
        tab.edDecimalLong.value = point.lon;
    });
        
    tab.edDecimalLat.onblur = tab.SubmitValues;
    tab.edDecimalLong.onblur = tab.SubmitValues;
}

function createAddressBox( locBox , canvas , styles ) {
    var tab = canvas;
    
    var addressDiv = document.createElement('div');
    
    var addressLabel = document.createElement('span');
    addressLabel.className = 'label';
    addressLabel.innerHTML = 'Address: ';
    addressDiv.appendChild(addressLabel);

    tab.edAddress = createInput(addressDiv);
    tab.edAddress.style.width = styles.addressBox.width;

    var searchButton = document.createElement('input');        
    searchButton.setAttribute('type', 'button');
    searchButton.setAttribute('value', 'Find');
    addressDiv.appendChild(searchButton);

    
    tab.resultMessage = document.createElement('span');
    addressDiv.appendChild(tab.resultMessage);
    tab.resultMessage.className = 'label';
    tab.resultMessage.style.display = 'none';

    tab.appendChild(addressDiv);
    
    // event handlers
    tab.LatLongCallback = function(point) {
        if (point != null)
        {
            locBox.SetLocation( point.y, point.x );

            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "green";
            tab.resultMessage.innerHTML = "Coordinates retrieval from address was successfull.";
        }
        else
        {
            tab.resultMessage.style.display = "block";
            tab.resultMessage.style.color = "red";
            tab.resultMessage.innerHTML = "Couldn't translate address to coordinates, please try again with a different address.";
        }    
    };
    tab.edAddress.onkeypress = function(e) {
        tab.resultMessage.style.display = "none";
        var charPressed;
        if (window.event)
          charPressed = window.event.keyCode;
        else
          charPressed = e.which;
        if (charPressed == 13) // Enter is pressed?
          tab.PostalAddress2LatLong();
    }
    tab.PostalAddress2LatLong = function() {
        var geocoder = new GClientGeocoder();
        var elem = tab.edAddress;
        postalAddressValue = elem.value;
        tab.mapLoaded = false;
        geocoder.getLatLng(postalAddressValue, tab.LatLongCallback);
        return false;    
    }; 

    searchButton.onclick = tab.PostalAddress2LatLong;
}

function DefaultIfEmpty(source, def) {
    // TODO trim spaces
    if (source == null || source.length == 0) {
        return def;
    } else {
        return source;
    }
}

// Geo Utils

function Sign(a) {
    if(a < 0)
    {
        return -1;
    }
    else
    {
        return 1;
    }
}

function isValidDecimalLat(lat)
{
    if (lat < -90 || lat > 90)
    {
        return false;
    }
    return true;
}

function isValidDecimalLong(lon)
{
    if (lon < -180 || lon > 180)
    {
        return false;
    }
    return true;
}

// DecimalToDms :: {lat:{degrees,minutes,seconds},lon:{degrees,minutes,seconds}} -> {lat,lon}
function DmsToDecimal( dmsPoint )
{
    var result = new Object();
    result.lat = Angle_DmsToDecimal(dmsPoint.lat);
    result.lon = Angle_DmsToDecimal(dmsPoint.lon);
    return result;
}

// Angle_DmsToDecimal :: {degrees,minutes,seconds} -> float
function Angle_DmsToDecimal( coord )
{
    var sign = Sign(coord.degrees);
    var degrees = Math.abs( Math.round(coord.degrees * 1000000.));
    var minutes = Math.abs( Math.round(coord.minutes * 1000000.));
    var seconds = Math.abs( Math.round(coord.seconds * 1000000.));
    return Math.round(degrees + (minutes/60.) + (seconds/3600.) ) * sign/1000000;
}

// DecimalToDms :: {lat,lon} -> {lat:{degrees,minutes,seconds},lon:{degrees,minutes,seconds}} 
function DecimalToDms( point ) 
{
    var result = new Object();
    result.lat = Angle_DecimalToDms(point.lat);
    result.lon = Angle_DecimalToDms(point.lon);
    return result;
}

// Angle_DecimalToDms :: float -> {degrees,minutes,seconds}
function Angle_DecimalToDms(coord)
{
    var result = new Object();
    var sign = Sign(coord);    
    coord = Math.abs( Math.round(coord * 1000000.))
    result.degrees = (Math.floor(coord / 1000000) * sign);
    result.minutes = Math.floor(  ((coord/1000000) - Math.floor(coord/1000000)) * 60);
    result.seconds = ( Math.floor(((((coord/1000000) - Math.floor(coord/1000000)) * 60) - Math.floor(((coord/1000000) - Math.floor(coord/1000000)) * 60)) * 100000) *60/100000 );
    return result;
}